<?php
// +----------------------------------------------------------------------
// | INPHP
// | Copyright (c) 2023 https://inphp.cc All rights reserved.
// | Author: 幺月儿(https://gitee.com/lulanyin) Email: inphp@qq.com
// | 该文件的开源协议以所在项目的LICENSE文件为准，请遵守开源协议要求
// +----------------------------------------------------------------------
// | 分类管理
// +----------------------------------------------------------------------
namespace app\cms\http\api;

use app\admin\attributes\auth;
use app\cms\helper\cache;
use app\cms\model\ArchiveModel;
use app\cms\model\CategoryModel;
use Inphp\Core\Db\PDO\Query;
use Inphp\Core\Object\Message;
use Inphp\Core\Util\Str;

class category
{
    /**
     * 登陆账号的UID
     * @var int
     */
    public int $uid = 0;

    /**
     * 获取单个分类详情
     * @param array|null $params
     * @return Message
     */
    public function index(?array $params = null): Message
    {
        //参数
        $params = $params ?? getClient()->get;
        //优先从缓存获取
        $cache = cache::get($params, "category");
        if ($cache) {
            //缓存中不为空时，直接返回
            return httpMessage($cache);
        }
        //
        $id = $params["id"] ?? ($params["index"] ?? 0);
        if (empty($id)) {
            return httpMessage("缺少ID参数");
        }
        //构建查询
        $db = CategoryModel::emptyQuery("c")
            ->where(is_numeric($id) ? "c.id" : "c.index", $id);
        //非后台管理，仅能取得对应站点的分类数据
        $user = authUserInfo();
        $admin = $params["admin"] ?? 0;
        $siteId = $params["siteId"] ?? 0;
        $siteId = is_numeric($siteId) && $siteId > 0 ? ceil($siteId) : 0;
        if ($admin != 1 || is_null($user) || $user["admin"] == 0) {
            //需要自动识别站点ID
            if ($siteId <= 0) {
                $siteId = matchCMSSite();
            }
            if ($siteId <= 0) {
                return httpMessage("未识别出站点ID");
            }
            $db->where(function (Query $query) use($siteId) {
                $query->whereNumberInSet([$siteId, 0], "c.siteId")
                    ->orWhereEmpty("c.siteId");
            });
        }
        $item = $db->first();
        if (empty($item)) {
            return httpMessage("未找到分类");
        }
        $item["pictures"] = attachmentsUrl($item["pictures"]);
        $item["preview"] = !empty($item["pictures"]) ? $item["pictures"][0] : null;
        if ($admin == 0) {
            //生成链接
            $item["url"] = makeCMSUrl($item, "category");
        }
        $item["detail"] = decodeHtml($item["detail"]);
        //取所有父级
        $item["parents"] = [];
        if (!empty($item["parentsId"])) {
            $parents = CategoryModel::emptyQuery()
                ->whereIn("id", $item["parentsId"])
                ->orderBy("floor", "asc")
                ->select(["`id`, `name`, `url`, `index`"])->get();
            if ($admin == 0) {
                foreach ($parents as &$parent) {
                    //生成链接
                    $parent["url"] = makeCMSUrl($parent, "category");
                }
            }
            $item["parents"] = $parents;
        }
        //保存至缓存
        cache::set($params, $item, "category");
        return httpMessage($item);
    }

    /**
     * 分类列表
     * 因分类支持多站点归属，不关联查询站点表，生成的链接，仅使用路径
     * @param array|null $params
     * @return array
     */
    public function list(?array $params = null): array
    {
        //参数
        $params = $params ?? getClient()->get;
        //优先从缓存获取
        $cache = cache::get($params, "category/list");
        if ($cache) {
            //缓存中不为空时，直接返回
            return $cache;
        }
        //构建查询
        $db = CategoryModel::emptyQuery("c")
            //->join(SiteModel::tableName()." s", "c.siteId=s.id")
            ->leftJoin(CategoryModel::tableName()." p", "c.parentId=p.id")
            ->select([
                "c.id, c.siteId, c.parentId, c.parentsId, c.hasChild, c.floor, c.name, c.index, c.icon, c.weight, c.template, c.templateArchive, c.url, c.pictures, c.visible",
                //站点信息
                //"s.name as siteName, s.host as siteHost, s.https as siteHttps"
            ]);
        //父级参数
        $parentId = $params["parentId"] ?? 0;
        if (!is_null($parentId)) {
            $db->where(is_numeric($parentId) ? "c.parentId" : "p.index", $parentId);
        }
        //
        $user = authUserInfo();
        //后台可以使用站点筛选，默认返回通用的分类
        $admin = $params["admin"] ?? 0;
        //站点ID
        $siteId = $params["siteId"] ?? 0;
        //判断处理
        if ($admin == 1 && !is_null($user) && $user["admin"] == 1) {
            //后台管理
            $siteId = !empty($siteId) ? Str::trim($siteId) : null;
            $sids = [];
            if (!empty($siteId)) {
                $siteIds = explode(",", $siteId);
                foreach ($siteIds as $id) {
                    if (is_numeric($id) && $id > 0 && !in_array(ceil($id), $sids)) {
                        $sids[] = ceil($id);
                    }
                }
            }
            if (!empty($sids)) {
                $db->where(function (Query $q) use($sids) {
                    $q->whereNumberInSet($sids, "c.siteId")
                        ->orWhereEmpty("c.siteId");
                });
            } else {
                $db->whereEmpty("c.siteId");
            }
        } else {
            $admin = 0;
            //非后台获取，需要进行SITE ID处理，并且仅支持固定SITE ID
            //优先使用GET参数，再到origin，再到host
            $siteId = is_numeric($siteId) && $siteId > 0 ? ceil($siteId) : 0;
            if ($siteId <= 0) {
                $site = matchCMSSite();
                if (!empty($site)) {
                    $siteId = $site["id"];
                }
            }
            if ($siteId === 0) {
                return [];
            }
            $db->where(function (Query $query) use($siteId) {
                $query->whereNumberInSet([$siteId, 0], "c.siteId")
                    ->orWhereEmpty("c.siteId");
            });
            //仅显示可显示的分类
            $db->where("c.visible", 1);
        }
        //最多取多少个
        $total = $params["total"] ?? 0;
        $total = is_numeric($total) && $total > 0 ? ceil($total) : 0;
        $offset = $params["offset"] ?? 0;
        $offset = is_numeric($offset) && $offset > 0 ? ceil($offset) : 0;
        //获取列表
        $list = $db->orderBy("c.weight", "asc")->get($total, $offset);
        //是否获取全部
        $all = $params["all"] ?? ($params["allLevel"] ?? 0);
        $all = $all == 1;
        //遍历处理
        foreach ($list as &$item) {
            $item["pictures"] = attachmentsUrl($item["pictures"]);
            $item["preview"] = !empty($item["pictures"]) ? $item["pictures"][0] : null;
            if ($admin == 0) {
                //生成链接
                $item["url"] = makeCMSUrl($item, "category");
            }
            //链接处理
            if ($item["hasChild"] == 1 && $all) {
                $item["subList"] = $this->subList($item["id"], $admin);
            }
        }
        //先保存至缓存
        cache::set($params, $list, "category/list");
        //返回列表
        return $list;
    }

    /**
     * 取下级栏目
     * @param int $parentId
     * @param int $admin
     * @return array
     */
    private function subList(int $parentId, int $admin): array
    {
        $list = CategoryModel::emptyQuery("c")
            ->select([
                "c.id, c.siteId, c.parentId, c.parentsId, c.hasChild, c.floor, c.name, c.index, c.icon, c.weight, c.template, c.templateArchive, c.url, c.pictures, c.visible"
            ])->where("c.parentId", $parentId)
            ->orderBy("c.weight", "asc")
            ->get();
        //遍历处理
        foreach ($list as &$item) {
            $item["pictures"] = attachmentsUrl($item["pictures"]);
            $item["preview"] = !empty($item["pictures"]) ? $item["pictures"][0] : null;
            if ($admin == 0) {
                //生成链接
                $item["url"] = makeCMSUrl($item, "category");
            }
            //链接处理
            if ($item["hasChild"] == 1) {
                $item["subList"] = $this->subList($item["id"], $admin);
            }
        }
        //返回数组
        return $list;
    }

    /**
     * 保存单个分类详细
     * @return Message
     */
    #[auth("cms/category")] public function save(): Message
    {
        //站点ID，多站点归属，或通用，或指定某个站点
        $siteId = POST("siteId");
        $siteId = !empty($siteId) ? Str::trim($siteId) : null;
        //父级
        $parentId = POST("parentId");
        $parentId = is_numeric($parentId) && $parentId > 0 ? ceil($parentId) : 0;
        //名称
        $name = POST("name");
        $name = !empty($name) ? Str::trim($name) : null;
        if (empty($name)) {
            return httpMessage("缺少分类名称");
        }
        $name = mb_strlen($name) > 30 ? mb_substr($name, 0, 30) : $name;
        //指定索引名
        $index = POST("index");
        $index = !empty($index) ? trim($index) : null;
        $index = !empty($index) ? (mb_strlen($index) > 30 ? mb_substr($index, 0, 30) : $index) : null;
        //图片
        $pictures = POST("pictures");
        $pictures = !empty($pictures) ? parseUploadFilesUrl($pictures) : null;
        $pictures = is_array($pictures) ? join(",", $pictures) : $pictures;
        //自定义关键词
        $keyword = POST("keyword");
        $keyword = is_array($keyword) ? $keyword : (!empty($keyword) ? explode(",", $keyword) : []);
        $keyword = !empty($keyword) ? join(",", $keyword) : null;
        //自定义摘要
        $description = POST("description");
        $description = !empty($description) ? trim($description) : null;
        if (!empty($description) && mb_strlen($description) > 500) {
            $description = mb_substr($description, 0, 500);
        }
        //
        //详情
        $detail = POST("detail");
        $detail = !empty($detail) ? encodeHtml($detail) : null;
        //使用模板
        $template = POST("template");
        $template = !empty($template) ? Str::trim($template) : null;
        $templateArchive = POST("templateArchive");
        $templateArchive = !empty($templateArchive) ? Str::trim($templateArchive) : null;
        //检验数据
        //父级
        $floor = 0;
        $parentsId = null;
        if ($parentId > 0) {
            //校验数据
            $parent = CategoryModel::getRowByPrimaryKey($parentId);
            if (empty($parent)) {
                $parentId = 0;
            } else {
                $parentsId = !empty($parent["parentsId"]) ? "{$parentId},{$parent["parentsId"]}" : $parentId;
                //站点ID要使用父级的
                $siteId = $parent["siteId"];
                $floor = floor($parent["floor"] + 1);
            }
        }
        if (!empty($siteId)) {
            //处理SiteId
            $sitesId = explode(",", $siteId);
            $sids = [];
            foreach ($sitesId as $sid) {
                if (is_numeric($sid) && $sid > 0 && !in_array(ceil($sid), $sids)) {
                    $sids[] = $sid;
                }
            }
            $siteId = !empty($sids) ? join(",", $sids) : null;
        }
        //
        $data = [
            "siteId"        => $siteId,
            "parentId"      => $parentId,
            "parentsId"     => $parentsId,
            "name"          => $name,
            "index"         => $index,
            "pictures"      => $pictures,
            "keyword"       => $keyword,
            "description"   => $description,
            "detail"        => $detail,
            "template"      => $template,
            "templateArchive" => $templateArchive,
            "floor"         => $floor
        ];
        //保存处理
        $id = POST("id");
        $id = is_numeric($id) && $id > 0 ? ceil($id) : 0;
        //
        if ($id > 0) {
            //先查询旧的数据
            $exists = CategoryModel::getRowByPrimaryKey($id);
            if (empty($exists)) {
                return httpMessage("ID无效，不存在该分类");
            }
            $db = CategoryModel::emptyQuery()->where("id", $id);
            if (!$db->update($data)) {
                return httpMessage("保存分类失败");
            }
            //如果有其中一个变更过，就需要同步
            if ($exists["siteId"] != $data["siteId"] || $exists["parentId"] != $data["parentId"]) {
                //在修改分类成功之后
                //1. 将所有的下级分类的 siteId 和 parentsId 都修同步一遍
                //2. 在同步每一个分类的时候，将该分类下的所有的文章的 siteId 和 categoriesId 都同步一遍
                CategoryModel::syncFromParent($id);
                //同步文章的
                @ArchiveModel::emptyQuery()
                    ->where("categoryId", $id)
                    ->update([
                        "siteId"        => !empty($siteId) ? $siteId : null,
                        "categoriesId"  => !empty($parentsId) ? "{$id},{$parentsId}" : $id
                    ]);
                //同步一次标签统计
            }
            if ($exists["parentId"] != $data["parentId"]) {
                //更新父级
                CategoryModel::setHasChild([$exists["parentId"], $data["parentId"]]);
            }
            //清除缓存
            cache::clean("category");
            return httpMessage(0, "保存成功");
        } else {
            $data["uid"] = $this->uid;
            $db = CategoryModel::emptyQuery();
            if (!$db->insert($data)) {
                return httpMessage("添加分类失败");
            }
            //
            if ($parentId > 0) {
                CategoryModel::setHasChild($parentId, 1);
            }
            //清除缓存
            cache::clean("category");
            return httpMessage(0, "添加成功", [
                "id"    => $db->getLastInsertId()
            ]);
        }
    }

    /**
     * 保存一个列表数据
     * 仅支持基础数据保存，结构参数列表获取，支持多层级一次性保存
     * @param int $parentId         指定上级ID，在多层级结构一次性保存的时候需要使用
     * @param array|null $list      列表数据
     * @return Message
     */
    #[auth("cms/category")] public function saveList(int $parentId = -1, ?array $list = null): Message
    {
        $list = $list ?? POST("list");
        $list = is_array($list) ? $list : (!empty($list) ? (@json_decode($list, true) ?? []) : []);
        if (empty($list)) {
            return httpMessage("没有数据需要保存");
        }
        $siteId = POST("siteId");
        $siteId = is_numeric($siteId) && $siteId > 0 ? ceil($siteId) : 0;
        $fail = $success = 0;
        //临时用
        $parents = [];
        //遍历处理
        foreach ($list as $item) {
            if (is_array($item)) {
                $id = $item["id"] ?? 0;
                $sid = $item["siteId"] ?? 0;
                $sid = is_numeric($sid) ? ceil($sid) : 0;
                $sid = $sid > 0 ? $sid : $siteId;
                $pid = $parentId > 0 ? $parentId : ($item["parentId"] ?? 0);
                $pid = is_numeric($pid) ? ceil($pid) : 0;
                $index = $item["index"] ?? null;
                $name = $item["name"] ?? null;
                $url = $item["url"] ?? null;
                $visible = $item["visible"] ?? 1;
                $weight = $item["weight"] ?? 50;
                $icon = $item["icon"] ?? null;
                $weight = is_numeric($weight) ? ceil($weight) : 50;
                //处理数据
                if (empty($name)) {
                    continue;
                }
                //限长
                $name = mb_strlen($name) > 30 ? mb_substr($name, 0, 30) : $name;
                $db = CategoryModel::emptyQuery();
                $method = "insert";
                $data = [
                    "icon"  => $icon,
                    "name"  => $name,
                    "index" => $index,
                    "weight"=> $weight,
                    "url"   => $url,
                    "visible" => $visible == 1 ? 1 : 0
                ];
                if ($id > 0) {
                    $db->where("id", $id)->set($data);
                    $method = "update";
                } else {
                    $data["siteId"] = $sid;
                    //取父级
                    if ($pid > 0 && !isset($parents[$pid])) {
                        $parents[$pid] = CategoryModel::getRowByPrimaryKey($pid);
                    }
                    if (!empty($parents[$pid])) {
                        $data["parentId"] = $parents[$pid]["id"];
                        $data["parentsId"] = !empty($parents[$pid]["parentsId"]) ? "{$parents[$pid]["id"]},{$parents[$pid]["parentsId"]}" : $parents[$pid]["id"];
                        $data["siteId"] = $parents[$pid]["siteId"];
                        $data["floor"] = floor($parents[$pid]["floor"] + 1);
                    }
                    $data["siteId"] = is_numeric($data["siteId"]) && $data["siteId"] > 0 ? $data["siteId"] : null;
                }
                if (!$db->{$method}($data)) {
                    $fail++;
                } else {
                    $success += $db->getAffectRows();
                    $id = $id > 0 ? $id : $db->getLastInsertId();
                    //
                    if ($method === "insert" && isset($data["parentId"]) && $data["parentId"]> 0) {
                        CategoryModel::setHasChild($data["parentId"], 1);
                    }
                    //如果存在下级，将下级数据一并保存
                    if (isset($item["subList"])) {
                        @$this->saveList($id, $item["subList"]);
                    }
                }
            }
        }
        //清除缓存
        cache::clean("category");
        return httpMessage(0, "已保存数据");
    }

    /**
     * 修改分类显示状态
     * @return Message
     */
    #[auth("cms/category")] public function switch(): Message
    {
        $id = POST("id");
        $id = is_numeric($id) && $id > 0 ? ceil($id) : 0;
        if ($id <= 0) {
            return httpMessage("ID参数无效");
        }
        $db = CategoryModel::emptyQuery()->where("id", $id)->setRaw("visible", "if(`visible`=1, 0, 1)");
        if ($db->update()) {
            //清除缓存
            cache::clean("category");
            return httpMessage(0, "操作成功");
        }
        return httpMessage("操作失败");
    }

    /**
     * 删除分类
     * @return Message
     */
    #[auth("cms/category")] public function delete(): Message
    {
        //
        $id = POST("id");
        $id = is_numeric($id) && $id > 0 ? ceil($id) : 0;
        if ($id <= 0) {
            return httpMessage("ID参数无效");
        }
        //获取
        $row = CategoryModel::getRowByPrimaryKey($id);
        if (empty($row)) {
            return httpMessage("ID无效");
        }
        $db = CategoryModel::emptyQuery()
            ->where("id", $id)
            ->orWhereFindInSet($id, "parentsId");
        if ($db->delete()) {
            //更新父级
            if ($row["parentId"] > 0) {
                CategoryModel::setHasChild($row["parentId"]);
            }
            //清除缓存
            cache::clean("category");
            return httpMessage(0, "删除了{$db->getAffectRows()}行数据");
        }
        return httpMessage("删除失败");
    }
}